﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media.Media3D;
using System.Windows;
using System.Windows.Media;
using Eniux.Windows.ThreeDEngine.Helper;
using System.Windows.Controls;
using Eniux.Windows.ThreeDEngine.AttachObject;

namespace Eniux.Windows.ThreeDEngine.Visual3Ds
{
    /// <summary>
    /// 屏幕空间抽象类
    /// </summary>
    public abstract class ScreenSpaceVisual3D : ModelVisual3DExtend
    {
        #region 变量
        /// <summary>
        ///   The clipping object.
        /// </summary>
        protected CohenSutherlandClippingAlgorithm clipping;

        /// <summary>
        ///  生成3-D形状的三角形基元
        /// </summary>
        protected MeshGeometry3D mesh;

        /// <summary>
        ///  几何模型
        /// </summary>
        protected GeometryModel3D model;

        /// <summary>
        /// 是否渲染标志
        /// </summary>
        private bool isRendering;
        #endregion

        #region 注册依赖属性

        #region Color
        /// <summary>
        /// 颜色属性
        /// </summary>
        public static readonly DependencyProperty ColorProperty = DependencyProperty.Register(
            "Color", typeof(Color), typeof(ScreenSpaceVisual3D), new UIPropertyMetadata(Colors.Black, ColorChanged));

        /// <summary>
        /// 获取或设置颜色
        /// </summary>
        public Color Color
        {
            get{return (Color)GetValue(ColorProperty);}
            set{SetValue(ColorProperty, value);}
        }

        #region DepthOffset
        /// <summary>
        ///   深度偏移属性
        /// </summary>
        public static readonly DependencyProperty DepthOffsetProperty = DependencyProperty.Register(
            "DepthOffset", typeof(double), typeof(ScreenSpaceVisual3D), new UIPropertyMetadata(0.0, GeometryChanged));

        /// <summary>
        /// 获取或设置深度偏移属性
        /// </summary>
        public double DepthOffset
        {
            get{ return (double)this.GetValue(DepthOffsetProperty);}
            set{this.SetValue(DepthOffsetProperty, value);}
        } 
        #endregion

        #region Points
        /// <summary>
        ///  点集合属性
        /// </summary>
        public static readonly DependencyProperty PointsProperty = DependencyProperty.Register(
            "Points",
            typeof(Point3DCollection),
            typeof(ScreenSpaceVisual3D),
            new UIPropertyMetadata(new Point3DCollection(), GeometryChanged));

        /// <summary>
        /// 获取或设置点集合属性
        /// </summary>
        public Point3DCollection Points
        {
            get{return (Point3DCollection)GetValue(PointsProperty);}
            set{SetValue(PointsProperty, value);}
        } 
        #endregion

        #endregion

        #endregion

        #region 构造、析构

        /// <summary>
        /// 默认构造函数
        /// </summary>
        protected ScreenSpaceVisual3D()
        {
            this.mesh = new MeshGeometry3D();
            GeometryModel3D geometry3d = new GeometryModel3D { Geometry = this.mesh };
            HitElement3D = new ModelUIElement3D();
            HitElement3D.Model = geometry3d;
            this.model = geometry3d;
            this.Children.Add(HitElement3D);
            this.OnColorChanged();
        }

        #endregion

        #region 公共属性

        public bool IsRendering
        {
            get
            {
                return this.isRendering;
            }

            set
            {
                if (value != this.isRendering)
                {
                    this.isRendering = value;
                    if (this.isRendering)
                    {
                        CompositionTarget.Rendering += this.CompositionTargetRendering;
                    }
                    else
                    {
                        CompositionTarget.Rendering -= this.CompositionTargetRendering;
                    }
                }
            }
        }

        #endregion

        #region 私有方法

        protected static void GeometryChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((ScreenSpaceVisual3D)d).OnGeometryChanged();
        }

        private static void ColorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((ScreenSpaceVisual3D)d).OnColorChanged();
        }

        private void CompositionTargetRendering(object sender, EventArgs e)
        {
            if (this.isRendering)
            {
                if (!Visual3DHelper.IsAttachedToViewport3D(this))
                {
                    return;
                }

                if (this.UpdateTransforms())
                {
                    this.UpdateClipping();
                    this.UpdateGeometry();
                }
            }
        }

        /// <summary>
        /// 颜色改变时，改变材质
        /// </summary>
        private void OnColorChanged()
        {
            var mg = new MaterialGroup();
            mg.Children.Add(new DiffuseMaterial(Brushes.Black));
            mg.Children.Add(new EmissiveMaterial(new SolidColorBrush(this.Color)));
            this.model.Material = mg;
        }

        private void OnGeometryChanged()
        {
            this.UpdateGeometry();
        }

        private void UpdateClipping()
        {
            Viewport3D vp = Visual3DHelper.GetViewport3D(this);
            if (vp == null)
            {
                return;
            }

            this.clipping = new CohenSutherlandClippingAlgorithm(10, vp.ActualWidth - 20, 10, vp.ActualHeight - 20);
        }

        #endregion

        #region 重写

        #region 重写方法

        protected override void OnVisualParentChanged(DependencyObject oldParent)
        {
            base.OnVisualParentChanged(oldParent);
            var parent = VisualTreeHelper.GetParent(this);
            this.IsRendering = parent != null;
        } 

        #endregion

        #region 重写属性

        /// <summary>
        /// 可接受命中测试的3D模型
        /// </summary>
        public override ModelUIElement3D HitElement3D { get; set; }

        #endregion

        #endregion

        #region 抽象方法

        protected abstract void UpdateGeometry();

        protected abstract bool UpdateTransforms();

        #endregion
    }
}

